#include "edit_template_interface.h"
#include "table.h"
#include "menu.h"
#include "io.h"
#include "deployment.h"
#include "template_set.h"
#include "ship_template.h"
#include "slot_template.h"
#include <fstream>
#include "json/writer.h"

using namespace std;

// This can't be called unless a Deploy is available!
#define __SET (*(getSet()))

//-----------------------
// MAIN INTRO
//--------------------------

MainTemplateIntro::MainTemplateIntro( FSM * _fsm ) : FSMState(_fsm) { stateName = "MainTemplateIntro"; }

int MainTemplateIntro::update() {
	vector<string> choices = { "load a new Template Set", "create a new Template Set" };
	if ( __SET ) {
		choices.push_back("inspect Template Set: " + __SET->name);
		choices.push_back("save Template Set: " + __SET->name);
	}
	int choice = Menu::print(choices, true);

	switch ( choice ) {
		// Back
		case 0:
			fsm->transitionBack();
			break;
			// Load new Template Set
		case 1: {
					string filename;
					cout << "Please enter the name of the json file that contains the Template Set:\n\n";
					getline(cin, filename); cout << "\n";

					try {
						Json::Value sets = IO::loadTemplateSet(filename);
						vector<string> setsNames;

						// List all Fleets in file
						for ( unsigned int i = 0; i < sets.size(); i++)
							setsNames.push_back(sets[i]["Name"].asString());

						int choice = Menu::print(setsNames,true,"select Template Set")-1;
						if ( choice == -1 ) break;

						delete (__SET);

						__SET = new (nothrow) TemplateSet(sets[choice]);
					} catch ( runtime_error e ) {
						cout << "\nFailed to load the Template Set\n" << e.what() << "\n\n";
					}
				}
				break;
				// Create new Template Set
		case 2:
				delete __SET;
				__SET = new TemplateSet();
				cout << "Please enter the name of your Template Set:\n\n";
				getline(cin, __SET->name); cout << "\n";
				break;
				// Inspect Template Set
		case 3:
				fsm->transitionTo("InspectTemplateSet");
				break;
				// Save Template Set
		case 4: {
					string filename;
					cout << "Please enter the name of the json file that will contain the Template Set (note that this will not overwrite existing Sets):\n\n";
					getline(cin, filename); cout << "\n";
					Json::Value sets;
					try {
						sets = IO::loadJson(filename);
						bool save = true;
						int index = sets.size();
						for ( unsigned int i = 0; i < sets.size(); i++ ) {
							if ( sets[i]["Type"] == "TemplateSet" && 
								 sets[i]["Name"].asString() == __SET->name ) {
								cout << "Found a Template Set with the same name at index ["<< i+1 << "] in this file. Do you want to replace it or keep it?\n\n";
								vector<string> kr = {"replace the existing Template Set", "keep both without changing names"};
								int choice = Menu::print(kr, true);
								if ( choice == 0 ) { save = false; break; }
								else if ( choice == 1 ) { index = i; break; }	
							}
						}

						if ( !save ) break;

						sets[index] = __SET->convertToJson();
					}
					catch ( FiletypeError e ) {
						// No Template Sets here, just append ours
						sets[sets.size()] = __SET->convertToJson();
						break;
					}
					catch ( runtime_error e ) {
						cout << "Writing on new file..\n\n";
						// Prob new file
						sets[0u] = __SET->convertToJson();
					}
					ofstream file(filename, ios_base::trunc );
					file << sets;
					file.close();
				}
				break;
	}
	return choice;
}


//--------------------------
// INSPECT TEMPLATE SET
//--------------------------

InspectTemplateSet::InspectTemplateSet( FSM * _fsm ) : FSMState(_fsm) { stateName = "InspectTemplateSet"; }

void InspectTemplateSet::doEnter(int param) {
	Table::print(*__SET);
}

int InspectTemplateSet::update() {
	vector<string> choices = { "reprint the Template Set", "rename the Template Set", "add a new Ship Template"};
	if ( __SET->getShipTemplatesNumber() > 0 ) {
		choices.push_back("copy a Ship Template");
		choices.push_back("remove a Ship Template");
		choices.push_back("select a Ship Template");
		choices.push_back("edit a Ship Template's layout");
	}
	int choice = Menu::print(choices, true);

	switch ( choice ) {
		// Back
		case 0:
			fsm->transitionBack();
			break;
			// Reprint Template Set
		case 1:
			Table::print(*__SET);
			break;
			// Rename Template Set
		case 2:
			cout << "Please insert the new Template Set's name:\n\n";
			getline(cin, __SET->name); cout << "\n";
			break;
			// Add Ship Template
		case 3: {
					string shipName;
					cout << "Please insert the Ship Template's name:\n\n";
					getline(cin, shipName); cout << "\n";

					vector<string> choices2 = { "standard Light Ship", "standard Medium Ship", "standard Heavy Ship" };
					int type = Menu::print(choices2, false)-1;

					try {
						__SET->addShipTemplate(IO::loadStandardShipTemplate(type), shipName);
					}
					catch ( runtime_error e ) {
						cout << "Couldn't load the specified template. Nothing was done.\n\n";
						break;
					}

					Table::print(*__SET);			
				}
				break;
				// Copy Ship Template
		case 4:
				try {
					ShipTemplate newShip (*(__SET->getShipTemplate(
									Menu::print(__SET->getShipTemplateNames(), true, "copy")-1
									)));
					cout << "Please insert the Ship Template's name:\n\n";
					getline(cin, newShip.name); cout << "\n";

					__SET->addShipTemplate(newShip);	
				} catch ( exception e ) {}

				break;
				// Remove Ship Template
		case 5:
				try {
					__SET->rmShipTemplate( Menu::print(__SET->getShipTemplateNames(),true,"remove")-1);
					Table::print(*__SET);			
				} catch ( invalid_argument e ) {}

				break;
				// Select Ship Template
		case 6:
				fsm->transitionTo("InspectShipTemplate");
				break;
				// Edit Ship Template
		case 7:
				fsm->transitionTo("EditShipTemplate");
				break;
	}
	return choice;
}


//-------------------------
// INSPECT SHIP TEMPLATE
//--------------------------

InspectShipTemplate::InspectShipTemplate( FSM * _fsm ) : FSMState(_fsm) {
	stateName = "InspectShipTemplate";
	selectedShip = -1;
}

void InspectShipTemplate::doEnter(int param) {
	try {
		selectedShip = Menu::print(__SET->getShipTemplateNames(), true, "inspect") - 1;
		Table::print(*(__SET->getShipTemplate(selectedShip)));
	} catch ( invalid_argument e ) {
		selectedShip = -1;
	}
}

int InspectShipTemplate::update() {
	if ( selectedShip == -1 ) {
		fsm->transitionBack();
		return 0;
	}
#define __SHIPT (__SET->getShipTemplate(selectedShip))

	vector<string> choices = { "reprint the Ship Template", "rename the Ship Template", "modify Max HP", "edit the number of Control Towers", "edit this Ship Template's layout"};
	int choice = Menu::print(choices, true);

	switch ( choice ) {
		// Back
		case 0:
			fsm->transitionBack();
			break;
		// Reprint
		case 1:
			Table::print(*__SHIPT);
			break;
		// Rename
		case 2: {
				cout << "Please enter the new name for the Ship Template:\n\n";
				string name;
				getline(cin, name); cout << "\n";
				__SET->renameShipTemplate(selectedShip, name);
			}
			break;
		// Modify HP
		case 3: {
				cout << "Please enter the new max HP value:\n\n";
				__SET->replShipTemplate(selectedShip, 
										__SET->getShipTemplate(selectedShip)->setMaxHp(
												Menu::getValidInt(1,100)));
			}
			break;
		// Edit Tower Slots
		case 4: {
				cout << "Please enter the how many Tower slots you want:\n\n";
				__SET->replShipTemplate(selectedShip, 
										__SET->getShipTemplate(selectedShip)->setTowers(
												Menu::getValidInt(0,100)));
		}
		break;
		// Edit Layout
		case 5:
		fsm->transitionTo("EditShipTemplate", selectedShip);
		break;
		
	}
	return 0;
#undef __SHIPT
}


//--------------------------
// EDIT SHIP TEMPLATE
//--------------------------


EditShipTemplate::EditShipTemplate( FSM * _fsm ) : FSMState(_fsm) {
	stateName = "EditShipTemplate";
	selectedShip = -1;
}

void EditShipTemplate::doEnter(int param) {
	if ( param != -1 ) {
		selectedShip = param;
		return;
	}
	try {
		cout << "Please note that the changes made in the editor may not preserve particular Slot settings!\n\n";
		selectedShip = Menu::print(__SET->getShipTemplateNames(), true, "edit") - 1;
		Table::print(*(__SET->getShipTemplate(selectedShip)));
	} catch ( invalid_argument e ) {
		selectedShip = -1;
	}
}

int EditShipTemplate::update() {
#define __SHIPT (__SET->getShipTemplate(selectedShip))
	if ( selectedShip == -1 ) {
		fsm->transitionBack();
		return 0;
	}

	int side;
	{
		vector<string> choices = {"FRONT", "FRONT RIGHT ( and FRONT LEFT )", "BACK RIGHT ( and BACK LEFT )", "BACK", "BACK LEFT ( and BACK RIGHT )", "FRONT LEFT ( and FRONT RIGHT )" };
		side = Menu::print(choices, true, "edit the");
	}

	if ( side == 0 ) {
		fsm->transitionBack();
		return 0;
	}

	side--;

	int remove;

	{
		vector<string> addremove = { "add a Slot to this Side", "remove a Slot from this Side", "toggle a Slot loadability from this Side" };
		remove = Menu::print(addremove, true) - 1;
	}

	if ( remove == -1 ) return 0;

	if ( remove ) {
		int slot;
		{
			// Build choice list
			vector<string> slots;
			int nSlots = __SHIPT->getSlotTemplatesNumber(side);

			int baseInt = 1;
			for (int i=0; i < side; i++ )
				baseInt += __SHIPT->getSlotTemplatesNumber(i);

			for (int i=0; i < nSlots;i++)
				slots.push_back("Slot [" + std::to_string(baseInt+i) + "]" );
			// Print it	
			string text = (remove == 1) ? "remove" : "toggle";
			slot = Menu::print(slots, true, text) - 1;
		}
		if ( slot == -1 ) return 0;

		if ( remove == 1 )
			__SET->replShipTemplate(selectedShip,
				__SET->getShipTemplate(selectedShip)->rmSymmetricSlotTemplate(side, slot));
		else
			__SET->replShipTemplate(selectedShip,
				__SET->getShipTemplate(selectedShip)->toggleSymmetricSlotTemplate(side, slot));

		Table::print(*__SHIPT);
	}			
	else {
		int dirs;
		cout << "Select the configuration of the Slot:\n\n";
		{
			vector<string> addremove = { "[0,0,0]",
				"[1,0,0]",
				"[0,1,0]",
				"[1,1,0]",
				"[0,0,1]",
				"[1,0,1]",
				"[0,1,1]",
				"[1,1,1]" };

			dirs = Menu::print(addremove, false, "select the configuration") - 1;
		}
		// cout << "Select the Max HP for this Slot:\n\n";
		// SlotTemplate slot(dirs,Menu::getValidInt(1,2));
		cout << "Default Max HP for this Slot defaults at: 2\n\n";

		cout << "Do you want this Slot to allow Cannons? ( 1: Yes, 0: No )\n";
		bool ac = Menu::getValidInt(0,1);

		SlotTemplate slot(dirs,2,ac);

		__SET->replShipTemplate(selectedShip,
				__SET->getShipTemplate(selectedShip)->addSymmetricSlotTemplate(side, slot));

		Table::print(*__SHIPT);
	}
	return 0;
#undef __SHIPT
}

#undef __SET
